package com.ccnf.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.ccnf.core.bean.TokenParams;
import com.ccnf.core.exception.BizException;
import com.ccnf.core.exception.ErrorForwardException;
import com.ccnf.core.utils.SystemClock;
import com.ccnf.model.ao.SmsAO;
import com.ccnf.model.ao.SmsAuthAO;
import com.ccnf.model.entity.*;
import com.ccnf.model.enums.AuthModeEnum;
import com.ccnf.model.enums.AuthStatusEnum;
import com.ccnf.model.enums.RoleTypeEnum;
import com.ccnf.model.enums.UnionRoleEnum;
import com.ccnf.model.mapper.*;
import com.ccnf.service.InvitationService;
import com.ccnf.service.SaasService;
import com.ccnf.service.TokenService;
import com.ccnf.service.UserService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tencent.cloud.CosStsClient;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;

@Slf4j
@Service
public class SaasServiceImpl implements SaasService {
    /** 会管家应用配置 */
    @Value("${saas.appId}")
    private String saasAppId;
    @Value("${saas.appKey}")
    private String saasAppKey;

    /** 会管家请求地址 */
    @Value("${saas.loginUrl}")
    private String saasLoginUrl;// 登录地址
    @Value("${saas.userInfoUrl}")
    private String saasUserInfoUrl;// 获取用户信息地址
    @Value("${saas.shareUrl}")
    private String saasShareUrl;// 获取微信分享配置地址
    @Value("${saas.smsUrl}")
    private String saasSmsUrl;// 发送短信地址

    /** 腾讯云对象存储配置 */
    @Value("${cos.secretId}")
    private String cosSecretId;
    @Value("${cos.secretKey}")
    private String cosSecretKey;
    @Value("${cos.bucket}")
    private String cosBucket;
    @Value("${cos.region}")
    private String cosRegion;

    /** 前端页面地址 */
    @Value("${font.indexUrl}")
    private String fontIndexUrl;
    @Value("${font.authUrl}")
    private String fontAuthUrl;

    /** 当前应用地址 */
    @Value("${app.url}")
    private String appUrl;

    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private UserService userService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private InvitationService invitationService;

    @Autowired
    private PhoneCaptchaMapper phoneCaptchaMapper;
    @Autowired
    private ExpoMapper expoMapper;
    @Autowired
    private ExpoUserMapper expoUserMapper;
    @Autowired
    private AuthorizationMapper authorizationMapper;

    @Override
    public String saasLoginUrl(HttpServletRequest request) {
        String channelCode = request.getParameter("channelCode");
        if (!StringUtils.hasText(channelCode)) {
            String expoId = request.getParameter("expoId");
            if (!StringUtils.hasText(expoId)) {
                return "/public/error?message=缺失展会ID";
            } else {
                channelCode = expoMapper.selectById(Long.valueOf(expoId)).getChannelCode();
            }
        }
        if (channelCode == null) {
            throw new BizException("展会ID为空");
        }
        StringBuilder sb = new StringBuilder(saasLoginUrl + "?appid=" + saasAppId + "&channelcode=" + channelCode + "&authtype=useinfo");
        String callbackUrl = appUrl + request.getRequestURI().replace("entry", "callback");
        try {
            sb.append("&returnUrl=" + callbackUrl + "?" + URLEncoder.encode(request.getQueryString(), "utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    public SocialUser saasUserInfo(String unionId) {
        // sign的生成规则是 Md5(appid+userid+saasAppKey) 编码方式UTF-8,需要转成大写,并且把”-“替换为空.
        String str = saasAppId + unionId + saasAppKey;
        String sign = DigestUtils.md5DigestAsHex(str.getBytes()).toUpperCase().replaceAll("-", "");
        String json = restTemplate.getForObject(saasUserInfoUrl + "?appid=" + saasAppId + "&userid=" + unionId + "&sign=" + sign, String.class);
        try {
            JsonNode jsonNode = objectMapper.readTree(json);
            JsonNode body = jsonNode.findValue("Body");
            String openId = body.findValue("OpenId").asText();
            String nickname = body.findValue("NickName").asText();
            Integer sex = body.findValue("Sex").asInt();
            String province = body.findValue("Province").asText();
            String city = body.findValue("City").asText();
            String country = body.findValue("Country").asText();
            String headImgUrl = body.findValue("HeadImgUrl").asText();
            SocialUser socialUser = new SocialUser();
            socialUser.setOpenId(openId);
            socialUser.setNickname(nickname);
            socialUser.setSex(sex);
            socialUser.setProvince(province);
            socialUser.setCity(city);
            socialUser.setCountry(country);
            socialUser.setHeadImage(headImgUrl);
            socialUser.setUnionId(unionId);
            return socialUser;
        } catch (IOException e) {
            throw new BizException("错误返回数据");
        }
    }

    @Override
    public Map<String, String> saasShareConfig(TokenParams tokenParams, String shareUrl) {
        String channelCode = tokenParams.getChannelCode();
        String str = saasAppId + tokenParams.getChannelCode() + saasAppKey;
        String sign = DigestUtils.md5DigestAsHex(str.getBytes()).toUpperCase().replaceAll("-", "");
        String json = restTemplate.getForObject(saasShareUrl + "?appid=" + saasAppId + "&channelcode=" + channelCode + "&shareurl=" + shareUrl + "&sign=" + sign, String.class);
        try {
            JsonNode jsonNode = objectMapper.readTree(json);
            JsonNode body = jsonNode.findValue("Body");
            String appId = body.findValue("AppId").asText();
            String timestamp = body.findValue("Timestamp").asText();
            String nonceStr = body.findValue("NonceStr").asText();
            String signature = body.findValue("Signature").asText();
            Map<String, String> map = new HashMap<>();
            map.put("AppId", appId);
            map.put("Timestamp", timestamp);
            map.put("NonceStr", nonceStr);
            map.put("Signature", signature);
            return map;
        } catch (IOException e) {
            throw new BizException("错误返回数据");
        }
    }

    @Override
    public String handleSaasLoignReturn(Map<String, String> requestParams) {
        String params = requestParams.get("params");// 会管家返回的参数
        String channelCode;// 会管家展会编号
        SocialUser socialUser;
        try {
            JsonNode jsonNode = objectMapper.readTree(params);
            String union = jsonNode.findValue("userid").asText();// 会管家用户ID
            channelCode = jsonNode.findValue("channelcode").asText();// 会管家展会编号
            socialUser = saasUserInfo(union);// 获取微信用户信息
        } catch (Exception e) {
            return  "/public/error?message=会管家返回参数解析出错";
        }
        String expoIdStr = requestParams.get("expoId");// 获取参数的展会ID字符串
        Long expoId;// 展会ID
        if (!StringUtils.hasText(expoIdStr)) {
            // 参数没有获取到展会ID
            Expo expo = new Expo();
            expo.setChannelCode(channelCode);
            expoId = expoMapper.selectOne(new QueryWrapper<>(expo)).getId();// 查询展会ID
        } else {
            expoId = Long.valueOf(expoIdStr);// 转化展会字符串为数字类型
        }
        Long userId = userService.saveSocialUser(expoId, socialUser);// 保存微信用户信息,获取用户ID
        handlePhoneCheck(new TokenParams(userId, expoId, null));
        String token = tokenService.createTokenByJJwt(userId, expoId, channelCode);// 生成令牌
        String inviIdStr = requestParams.get("inviId");// 邀请函ID
        String type = requestParams.get("type");// 类型
        StringBuilder returnUrl = new StringBuilder(fontIndexUrl);// 前端首页地址
        try {
            if (StringUtils.hasText(type)) {
                if (type.equalsIgnoreCase("auth")) {
                    // 授权相关
                    String authCode = requestParams.get("authCode");// 授权码
                    if (StringUtils.hasText(authCode)) {
                        // 类型是授权
                        invitationService.handleUserAuth(authCode, expoId, userId);
                    }
                } else if (type.equalsIgnoreCase("share")) {
                    // 分享相关
                    String shareIdStr = requestParams.get("shareId");
                    if (StringUtils.hasText(shareIdStr)) {
                        Long shareId = Long.valueOf(shareIdStr);
                        invitationService.handleInvitationShare(expoId, shareId, userId);
                    }
                }
            }
            if (StringUtils.hasText(inviIdStr)) {
                return returnUrl.append("detail?inviId=")
                        .append(inviIdStr)
                        .append("&token=")
                        .append(token)
                        .toString();
            }
            returnUrl.append("?token=" + token);
        } catch (ErrorForwardException e) {
            try {
                return returnUrl.append("errpage?token=")
                        .append(token)
                        .append("&errCode=")
                        .append(e.getErrorCode())
                        .append("&errMsg=")
                        .append(URLEncoder.encode(e.getMessage(), "UTF-8")).toString();
            } catch (UnsupportedEncodingException e1) {
                e1.printStackTrace();
            }
        } catch (Exception e) {
            log.error("微信回调出现错误：", e);
        }
        return returnUrl.toString();
    }

    @Override
    public Map<String, Object> shareEntry(HttpServletRequest request, TokenParams tokenParams, String currentUrl) {
        Map<String, Object> data = new HashMap<>();
        Map<String, String> map = saasShareConfig(tokenParams, currentUrl);
        data.put("jssdk", map);
        data.put("shareUrl", appUrl + request.getRequestURI().replace("/share", "") + "?expoId=" + tokenParams.getExpoId());
        return data;
    }

    @Override
    public Map<String, Object> getTxCosConfig() {
        TreeMap<String, Object> config = new TreeMap<>();
        try {
            config.put("SecretId", cosSecretId);// 固定密钥
            config.put("SecretKey", cosSecretKey);// 固定密钥
            config.put("durationSeconds", 1800);// 临时密钥有效时长，单位是秒
            config.put("bucket", cosBucket);// 换成你的 bucket
            config.put("region", cosRegion);// 换成 bucket 所在地区
            // 这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的目录，例子：* 或者 a/* 或者 a.jpg
            config.put("allowPrefix", "*");
            // 密钥的权限列表。简单上传和分片需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
            String[] allowActions = new String[]{
                    // 简单上传
                    "name/cos:PutObject",
                    "name/cos:PostObject",
                    // 分片上传
                    "name/cos:InitiateMultipartUpload",
                    "name/cos:ListMultipartUploads",
                    "name/cos:ListParts",
                    "name/cos:UploadPart",
                    "name/cos:CompleteMultipartUpload"
            };
            config.put("allowActions", allowActions);
            JSONObject credential = CosStsClient.getCredential(config);
            return credential.toMap();
        } catch (Exception e) {
            throw new BizException("对象存储签名出错");
        }
    }

    @Override
    public boolean saasSendSms(Long expoId, String phone, String smsContent) {
        StringBuilder sb = new StringBuilder(saasSmsUrl);
        Long noncestr = IdWorker.getId();
        sb.append("?appid=" + saasAppId)
                .append("&tomobile=" + phone)
                .append("&noncestr=" + noncestr)
                .append("&msnmessage=" + smsContent)
                .append("【")
                .append(expoMapper.selectById(expoId).getExpoName())
                .append("】");
        // sign的生成规则是 Md5(appid + noncestr + tomobile+appkey) 编码方式UTF-8,需要转成大写,并且把”-“替换为空.
        String str = saasAppId + noncestr + phone + saasAppKey;
        String sign = DigestUtils.md5DigestAsHex(str.getBytes()).toUpperCase().replaceAll("-", "");
        sb.append("&sign=" + sign);
        String json = restTemplate.getForObject(sb.toString(), String.class);
        try {
            JsonNode jsonNode = objectMapper.readTree(json);
            if (!jsonNode.findValue("Code").asText().equals("200")) {
                log.error("短信发送失败：" + phone);
                return false;
            }
        } catch (IOException e) {
            return false;
        }
        return true;
    }

    @Override
    public void sendPhoneCaptcha(TokenParams tokenParams, String phone) {
        String captcha = String.valueOf(new Random().nextInt(899999) + 100000);
        String smsContent = "您的验证码是" + captcha + "，为了保护您的账户安全，请勿转发他人，如非本人操作，请忽略本短信。该验证码将在30分钟后失效。";
        boolean isSuccess = saasSendSms(tokenParams.getExpoId(), phone, smsContent);
        if (isSuccess) {
            PhoneCaptcha phoneCaptcha = phoneCaptchaMapper.selectById(phone);
            if (phoneCaptcha == null) {
                phoneCaptcha = new PhoneCaptcha();
                phoneCaptcha.setPhone(phone);
                phoneCaptcha.setCaptcha(captcha);
                phoneCaptcha.setExpireTime(SystemClock.second() + 1800);// 30分钟后过期
                phoneCaptchaMapper.insert(phoneCaptcha);
            } else {
                phoneCaptcha.setCaptcha(captcha);
                phoneCaptcha.setExpireTime(SystemClock.second() + 1800);// 30分钟后过期
                phoneCaptchaMapper.updateById(phoneCaptcha);
            }
        } else {
            throw new BizException("短信发送失败");
        }
    }

    @Override
    @Transactional
    public void verifyPhoneCaptcha(String phone, String captcha, TokenParams tokenParams) {
        PhoneCaptcha phoneCaptcha = phoneCaptchaMapper.selectById(phone);
        if (phoneCaptcha == null) {
            throw new BizException("验证码不存在");
        }
        if (!phoneCaptcha.getCaptcha().equals(captcha)) {
            throw new BizException("验证码无效");
        }
        if (phoneCaptcha.getExpireTime() < SystemClock.second()) {
            throw new BizException("验证码已失效");
        }
        phoneCaptcha.setExpireTime(0L);
        phoneCaptchaMapper.updateById(phoneCaptcha);
        ExpoUser expoUser = expoUserMapper.selectExpoUser(tokenParams.getExpoId(), tokenParams.getUserId());
        if (expoUser == null) {
            expoUser = new ExpoUser();
            expoUser.setUserId(tokenParams.getUserId());
            expoUser.setExpoId(tokenParams.getExpoId());
            expoUser.setPhone(phone);
            expoUserMapper.insert(expoUser);
        } else {
            expoUser.setPhone(phone);
            expoUserMapper.updateById(expoUser);
        }
        handlePhoneCheck(tokenParams);
    }

    @Autowired
    private InvitationUserMapper invitationUserMapper;

    private void handlePhoneCheck(TokenParams tokenParams) {
        Long expoId = tokenParams.getExpoId();
        Long userId = tokenParams.getUserId();
        RoleTypeEnum finalRoleType = RoleTypeEnum.USER;
        ExpoUser expoUser = expoUserMapper.selectExpoUser(expoId, userId);
        if (expoUser != null && StringUtils.hasText(expoUser.getPhone())) {
            String phone = expoUser.getPhone();
            Authorization authorization = new Authorization();
            authorization.setExpoId(expoId);
            authorization.setPhone(phone);
            authorization.setAuthStatus(AuthStatusEnum.NONE);
            List<Authorization> authorizations = authorizationMapper.selectList(new QueryWrapper<>(authorization));
            for (Authorization auth : authorizations) {
                RoleTypeEnum roleType = auth.getRoleType();
                if (finalRoleType.getRoleType() < roleType.getRoleType()) {
                    finalRoleType = roleType;
                }
                if (auth.getInvitationId() != null && auth.getInvitationId() != 0) {
                    InvitationUser invitationUser = new InvitationUser();
                    invitationUser.setInvitationId(auth.getInvitationId());
                    invitationUser.setUserId(userId);
                    InvitationUser selectOne = invitationUserMapper.selectOne(new QueryWrapper<>(invitationUser));
                    if (selectOne == null) {
                        invitationUser.setUnionRole(UnionRoleEnum.LICENSOR);
                        invitationUserMapper.insert(invitationUser);
                    } else {
                        UnionRoleEnum unionRole = selectOne.getUnionRole();
                        if (unionRole.equals(UnionRoleEnum.CREATOR)) {
                            throw new BizException(800, "自身不能授权邀请函操作");
                        }
                        selectOne.setUnionRole(UnionRoleEnum.LICENSOR);
                        invitationUserMapper.updateById(selectOne);
                    }
                }
                auth.setUserId(tokenParams.getUserId());
                auth.setAuthStatus(AuthStatusEnum.DONE);
                auth.setAuthTime(SystemClock.second());
                authorizationMapper.updateById(auth);
            }
            if (expoUser.getRoleType().getRoleType() < finalRoleType.getRoleType()) {
                expoUser.setRoleType(finalRoleType);
                expoUserMapper.updateById(expoUser);
            }
        }
    }

    @Override
    public List<SmsAO> smsUserAuth(SmsAuthAO smsAuthAO, TokenParams tokenParams) {
        Long expoId = tokenParams.getExpoId();// 展会ID
        Long userId = tokenParams.getUserId();// 授权用户ID
        RoleTypeEnum roleType = smsAuthAO.getRoleType();
        ExpoUser expoUser = expoUserMapper.selectExpoUser(expoId, userId);// 查询授权人角色信息
        if (expoUser.getRoleType().getRoleType() < roleType.getRoleType()) {
            throw new BizException("权限不足");
        } else {
            Authorization authorization = new Authorization();
            authorization.setRoleType(roleType);
            authorization.setExpoId(tokenParams.getExpoId());
            authorization.setAuthMode(AuthModeEnum.SMS);
            List<SmsAO> sms = smsAuthAO.getSms();
            Iterator<SmsAO> iterator = sms.iterator();
            while (iterator.hasNext()) {
                String phone = iterator.next().getPhone();
                boolean isSuccess = saasSendSms(expoId, phone, "尊敬的用户你好，你的好友\"" +
                        userService.getById(userId).getNickname() + "\"" + "邀请您加入邀请函应用管理，请关注公众号【会管家】，点击进入H5邀请函，输入您的手机号码：" + phone + "登录。如有疑问可以致电："
                        + expoUser.getPhone());
                if (isSuccess) {
                    authorization.setPhone(phone);
                    authorizationMapper.insert(authorization);
                    authorization.setId(null);
                    iterator.remove();
                }
            }
            return sms;
        }
    }
}